<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖函数测试</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        .test-section {
            margin: 20px 0;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        button {
            margin: 5px;
            padding: 10px 15px;
            cursor: pointer;
        }
        .output {
            background: #f5f5f5;
            padding: 10px;
            margin: 10px 0;
            border-radius: 3px;
            min-height: 20px;
        }
        .counter {
            font-weight: bold;
            color: #007bff;
        }
    </style>
</head>
<body>
    <h1>防抖函数测试</h1>
    
    <div class="test-section">
        <h3>基础防抖测试</h3>
        <p>快速点击按钮，只有最后一次点击后500ms才会执行</p>
        <button id="basicTest">快速点击我</button>
        <div class="output" id="basicOutput">执行次数: <span class="counter">0</span></div>
    </div>

    <div class="test-section">
        <h3>Leading Edge 测试</h3>
        <p>启用leading选项，第一次点击立即执行</p>
        <button id="leadingTest">点击测试Leading</button>
        <div class="output" id="leadingOutput">执行次数: <span class="counter">0</span></div>
    </div>

    <div class="test-section">
        <h3>取消和刷新测试</h3>
        <button id="cancelTest">点击后取消</button>
        <button id="cancelBtn">取消</button>
        <button id="flushBtn">立即执行</button>
        <div class="output" id="cancelOutput">执行次数: <span class="counter">0</span></div>
    </div>

    <div class="test-section">
        <h3>节流函数测试</h3>
        <p>每500ms最多执行一次</p>
        <button id="throttleTest">快速点击测试节流</button>
        <div class="output" id="throttleOutput">执行次数: <span class="counter">0</span></div>
    </div>

    <script type="module">
        // 防抖函数实现
        function debounce(func, delay, options = {}) {
            let timeoutId = null;
            let maxTimeoutId = null;
            let lastCallTime = null;
            let lastInvokeTime = 0;
            let lastArgs;
            let lastThis;
            let result;

            const { leading = false, trailing = true, maxWait } = options;

            function invokeFunc(time) {
                const args = lastArgs;
                const thisArg = lastThis;
                
                lastArgs = undefined;
                lastThis = undefined;
                lastInvokeTime = time;
                result = func.apply(thisArg, args);
                return result;
            }

            function leadingEdge(time) {
                lastInvokeTime = time;
                timeoutId = setTimeout(timerExpired, delay);
                return leading ? invokeFunc(time) : result;
            }

            function remainingWait(time) {
                const timeSinceLastCall = time - lastCallTime;
                const timeSinceLastInvoke = time - lastInvokeTime;
                const timeWaiting = delay - timeSinceLastCall;

                return maxWait !== undefined
                    ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
                    : timeWaiting;
            }

            function shouldInvoke(time) {
                const timeSinceLastCall = time - lastCallTime;
                const timeSinceLastInvoke = time - lastInvokeTime;

                return (
                    lastCallTime === null ||
                    timeSinceLastCall >= delay ||
                    timeSinceLastCall < 0 ||
                    (maxWait !== undefined && timeSinceLastInvoke >= maxWait)
                );
            }

            function timerExpired() {
                const time = Date.now();
                if (shouldInvoke(time)) {
                    return trailingEdge(time);
                }
                timeoutId = setTimeout(timerExpired, remainingWait(time));
            }

            function trailingEdge(time) {
                timeoutId = null;

                if (trailing && lastArgs) {
                    return invokeFunc(time);
                }
                lastArgs = undefined;
                lastThis = undefined;
                return result;
            }

            function cancel() {
                if (timeoutId !== null) {
                    clearTimeout(timeoutId);
                }
                if (maxTimeoutId !== null) {
                    clearTimeout(maxTimeoutId);
                }
                lastInvokeTime = 0;
                lastArgs = undefined;
                lastCallTime = null;
                lastThis = undefined;
                timeoutId = null;
                maxTimeoutId = null;
            }

            function flush() {
                return timeoutId === null ? result : trailingEdge(Date.now());
            }

            function pending() {
                return timeoutId !== null;
            }

            function debounced(...args) {
                const time = Date.now();
                const isInvoking = shouldInvoke(time);

                lastArgs = args;
                lastThis = this;
                lastCallTime = time;

                if (isInvoking) {
                    if (timeoutId === null) {
                        leadingEdge(lastCallTime);
                        return;
                    }
                    if (maxWait !== undefined) {
                        timeoutId = setTimeout(timerExpired, delay);
                        return invokeFunc(lastCallTime);
                    }
                }
                if (timeoutId === null) {
                    timeoutId = setTimeout(timerExpired, delay);
                }
            }

            debounced.cancel = cancel;
            debounced.flush = flush;
            debounced.pending = pending;

            return debounced;
        }

        // 节流函数
        function throttle(func, delay, options = {}) {
            return debounce(func, delay, {
                leading: options.leading !== false,
                trailing: options.trailing !== false,
                maxWait: delay
            });
        }

        // 测试用例
        let basicCount = 0;
        let leadingCount = 0;
        let cancelCount = 0;
        let throttleCount = 0;

        // 基础防抖测试
        const basicDebounced = debounce(() => {
            basicCount++;
            document.querySelector('#basicOutput .counter').textContent = basicCount;
        }, 500);

        document.getElementById('basicTest').addEventListener('click', basicDebounced);

        // Leading edge 测试
        const leadingDebounced = debounce(() => {
            leadingCount++;
            document.querySelector('#leadingOutput .counter').textContent = leadingCount;
        }, 500, { leading: true });

        document.getElementById('leadingTest').addEventListener('click', leadingDebounced);

        // 取消和刷新测试
        const cancelDebounced = debounce(() => {
            cancelCount++;
            document.querySelector('#cancelOutput .counter').textContent = cancelCount;
        }, 1000);

        document.getElementById('cancelTest').addEventListener('click', cancelDebounced);
        document.getElementById('cancelBtn').addEventListener('click', () => {
            cancelDebounced.cancel();
            console.log('已取消防抖函数');
        });
        document.getElementById('flushBtn').addEventListener('click', () => {
            cancelDebounced.flush();
            console.log('立即执行防抖函数');
        });

        // 节流测试
        const throttled = throttle(() => {
            throttleCount++;
            document.querySelector('#throttleOutput .counter').textContent = throttleCount;
        }, 500);

        document.getElementById('throttleTest').addEventListener('click', throttled);

        console.log('防抖函数测试页面已加载');
    </script>
</body>
</html>
